package com.cheney.common.sensitive.core;

import com.cheney.common.sensitive.handler.DefaultStringMessageHandler;
import com.cheney.common.sensitive.handler.LogbackMessageHandler;
import com.cheney.common.sensitive.properties.DefaultStringProperties;
import com.cheney.common.sensitive.util.SensitiveUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.CollectionUtils;

import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 脱敏配置类
 */
@Configuration
@ConfigurationProperties(prefix = "log.sensitive")
@ConditionalOnProperty(name = "log.sensitive.enable", havingValue = "true")
@ImportAutoConfiguration(ConverterConfiguration.class)
public class SensitiveConfiguration implements SmartInitializingSingleton {

    /**
     * 是否启用日志脱敏
     */
    private boolean enable = false;

    /**
     * 脱敏方式
     */
    private OptEnum opt = OptEnum.DEFAULT_STRING;

    /**
     * 要脱敏的loggerName的前缀
     * <p>
     * loggerName即：创建logger所用类的全类名
     * </p>
     * <p>
     * 此字段和{@link SensitiveConfiguration#excludeLoggerPrefix}字段搭配，可灵活控制脱敏范围
     * </p>
     */
    private Set<String> includeLoggerPrefix = new HashSet<>();

    /**
     * 不要脱敏的loggerName的前缀
     * <p>
     * loggerName即：创建logger所用类的全类名
     * </p>
     * 此字段和{@link SensitiveConfiguration#includeLoggerPrefix}字段搭配，可灵活控制脱敏范围
     * </p>
     */
    private Set<String> excludeLoggerPrefix = new HashSet<>();

    @NestedConfigurationProperty
    public DefaultStringProperties configString = new DefaultStringProperties();

    @Override
    public void afterSingletonsInstantiated() {
        // 装配
        LogbackMessageHandler handler;
        switch (opt) {
            case DEFAULT_STRING:
                Set<DefaultStringMessageHandler.KeyValueDelimiter> keyValueDelimiter = configString.getKeyValueDelimiter();
                if (CollectionUtils.isEmpty(keyValueDelimiter)) {
                    throw new IllegalArgumentException("Configuration item keyValueDelimiter cannot be empty.");
                }
                handler = new DefaultStringMessageHandler(SensitiveUtil.unfold(configString.getStrategies()),
                        configString.getShortCircuitPlugins(),
                        configString.isCompatBackslash(),
                        keyValueDelimiter);
                break;
            default:
                throw new IllegalArgumentException("Cannot recognize log.sensitive.opt [" + opt + "].");
        }
        // 赋值给logback消息转换器
        LogbackCoreConverter.setHandler(handler);
        LogbackCoreConverter.addIncludePackagePrefix(includeLoggerPrefix.stream().filter(StringUtils::isNotBlank).collect(Collectors.toSet()));
        LogbackCoreConverter.addExcludePackagePrefix(excludeLoggerPrefix.stream().filter(StringUtils::isNotBlank).collect(Collectors.toSet()));
    }

    /**
     * 脱敏方式
     */
    public enum OptEnum {

        /**
         * json脱敏
         */
        DEFAULT_JSON,

        /**
         * 字符串脱敏
         */
        DEFAULT_STRING,

    }


    public boolean isEnable() {
        return enable;
    }

    public void setEnable(boolean enable) {
        this.enable = enable;
    }

    public OptEnum getOpt() {
        return opt;
    }

    public void setOpt(OptEnum opt) {
        this.opt = opt;
    }

    public Set<String> getIncludeLoggerPrefix() {
        return includeLoggerPrefix;
    }

    public void setIncludeLoggerPrefix(Set<String> includeLoggerPrefix) {
        this.includeLoggerPrefix = includeLoggerPrefix;
    }

    public Set<String> getExcludeLoggerPrefix() {
        return excludeLoggerPrefix;
    }

    public void setExcludeLoggerPrefix(Set<String> excludeLoggerPrefix) {
        this.excludeLoggerPrefix = excludeLoggerPrefix;
    }

    public DefaultStringProperties getConfigString() {
        return configString;
    }

    public void setConfigString(DefaultStringProperties configString) {
        this.configString = configString;
    }

}